//
//  UorInXNetworkProtocol.swift
//  InVowCG
//
//  Created by dongyx on 2023/7/7.
//

import Foundation
import Combine
import UIKit
import CryptoSwift
@_exported import Alamofire
@_exported import HandyJSON

struct UorInXNetworkWarpper<Base> {
    let base: Base
}

protocol UorInXNetworkProtocol {  }
extension UorInXNetworkProtocol {
    
    var request: UorInXNetworkWarpper<Self> {
        get { UorInXNetworkWarpper(base: self) }
        set {  }
    }
    
}
extension UIViewController: UorInXNetworkProtocol { }

extension UorInXNetworkWarpper {
    
    /// 开始获取数据
    func start(_ config: RequestConfig, completion: ((_ result: UorInXNetworkResponseResult<[String: Any]>) -> Void)?) {
        Task {
            ToastTool.show()
            let result = await request(config)
            DispatchQueue.main.async {
                ToastTool.dismiss()
                completion?(result)
            }
        }
    }
    
    func start<Model: HandyJSON>(_ config: RequestConfig, type: Model.Type, listKey: String = "", isLoading: Bool = true, completion: ((_ result: UorInXNetworkResponseResult<Model>) -> Void)?) {
        Task {
            if isLoading {
                ToastTool.show()
            }
            let result = await request(config)
            DispatchQueue.main.async {
                if isLoading {
                    ToastTool.dismiss()
                }                
                if result.isSuccessful, let jsonModel = result.model {
                    print(jsonModel)
                    if !listKey.isEmpty, let infos = jsonModel[listKey] as? [[String: Any]] {
                        let models = [Model].deserialize(from: infos)?.compactMap({ $0 })
                        completion?(.successful(models))
                    } else {
                        let models = [Model].deserialize(from: result.models)?.compactMap({ $0 })
                        completion?(.successful(models))
                    }
                } else {
                    result.errorDescripton?.toast_error
                    completion?(.error(result.error))
                }
            }
        }
    }
    
    /// 上传图片
    func upload(image: UIImage?, isAvatar: Bool = false, completion: ((_ result: UorInXNetworkResponseResult<UorInXImageModel>) -> Void)?) {
        Task {
            ToastTool.show()
            let result = await request(.UploadPhoto(isHead: isAvatar), image: image)
            DispatchQueue.main.async {
                ToastTool.dismiss()
                if result.isSuccessful, let json = result.model {
                    if let model = UorInXImageModel.deserialize(from: json) {
                        completion?(.successful([model]))
                    }
                } else {
                    completion?(.error(result.error))
                }
            }
        }
    }
    
    /// 查询树形结构
    func queryTree<Model: HandyJSON>(_ config: RequestConfig, type: Model.Type, completion: ((_ result: UorInXNetworkResponseResult<Model>) -> Void)?) {
        
        func queryClassOrStructKey<JsonModel: HandyJSON>(model: JsonModel.Type) -> String {
            let targetName = (Bundle.main.infoDictionary?["CFBundleExecutable"] as? String) ?? "Jov"
            return "\(model.self)".replacingOccurrences(of: "\(targetName).", with: "")
        }
        
        let listKey = queryClassOrStructKey(model: type)
        
        start(config, type: UorInXDefaultFuncTreeModel.self) { result in
            if result.isSuccessful, let children = result.model?.children {
                var itemModels = [Model?]()
                children.forEach {
                    if let info = $0 as? [String: Any] {
                        if (info[RequestParameterConfig.shared.funcTreeKey] as? String) == listKey {
                            itemModels.append(Model.deserialize(from: info))
                        }
                    }
                }
                completion?(.successful(itemModels.compactMap({ $0 })))
            } else {
                completion?(.error(result.error))
            }
        }
    }
    
}

extension UorInXNetworkWarpper {
    
    private func request(_ config: RequestConfig, image: UIImage? = nil) async -> UorInXNetworkResponseResult<[String: Any]> {
        var request = AF.request(config.request)
        if config.isEncryptRequest {
            return await withCheckedContinuation({ continuation in
                request.responseString { response in
                    switch response.result {
                    case .success(let responseObject):
                        guard let code = response.response?.statusCode else { return }
                        guard code == 200 else {
                            continuation.resume(returning: .error(UorInXNetworkError(code: code, descripton: responseObject, request: config.request)))
                            return
                        }
                        guard responseObject.count > 0 else {
                            continuation.resume(returning: .error(UorInXNetworkError(code: 51001, descripton: "请求错误：响应为空", request: config.request)))
                            return
                        }
                        guard let x_sign = (response.response?.allHeaderFields["x-sign"] ?? response.response?.allHeaderFields["X-Sign"]) as? String else {
                            continuation.resume(returning: .error(UorInXNetworkError(code: 51001, descripton: "请求错误：响应头里面没有x-sign参数，可能是请求头参数错误造成", request: config.request)))
                            return
                        }
                        if let baseStr = response.value,
                           let data = Data(base64Encoded: baseStr, options: .ignoreUnknownCharacters),
                           let decodeArrayData = try? AES(key: config.DECRYPTKEY.bytes, blockMode: ECB(), padding: .pkcs7).decrypt(data.bytes),
                           let decodeString = String(data: Data(decodeArrayData), encoding: .utf8),
                           decodeString.HMAC(key: config.PUBLICKEY) == x_sign {
                            if let model = RequestInfoModel.deserialize(from: decodeString.convertInfo()) {
                                guard let jsonData = model.data, model.code == 0 else {
                                    continuation.resume(returning: .error(UorInXNetworkError(code: model.code, descripton: model.msg, request: config.request)))
                                    return
                                }
                                if case .DeleteAccount = config {
                                    /// 如果删除账号成功，移除token
                                    RequestParameterConfig.shared.requestToken = ""
                                }
                                // 解析模型
                                if let info = jsonData as? [String: Any] {
                                    continuation.resume(returning: .successful([info]))
                                } else if let infos = jsonData as? [[String: Any]] {
                                    continuation.resume(returning: .successful(infos))
                                } else {
                                    continuation.resume(returning: .successful([["value": jsonData]]))
                                }
                                return
                            }
                        } else {
                            continuation.resume(returning: .error(UorInXNetworkError(code: 21001, descripton: "解密验签失败", request: config.request)))
                            return
                        }
                    case .failure(let error):
                        continuation.resume(returning: analysisNetworkError(config, error: error))
                        return
                    }
                }
            })
        } else {
            if let imageData = image?.pngData() {
                // 上传图片
                request = AF.upload(multipartFormData: { multipartforData in
                    multipartforData.append(imageData, withName: "file", fileName: "avatar.jpg", mimeType: "image/jpeg")
                }, with: config.request)
            }
            return await withCheckedContinuation({ continuation in
                request.responseJSON { response in
                    switch response.result {
                    case .success(_):
                        guard response.value != nil,
                              let dict = response.value as? [String: Any],
                              let model = RequestInfoModel.deserialize(from: dict) else { return }
                        guard let jsonData = model.data, model.code == 0 else {
                            continuation.resume(returning: .error(UorInXNetworkError(code: model.code, descripton: model.msg, request: config.request)))
                            return
                        }
                        if let info = jsonData as? [String: Any] {
                            continuation.resume(returning: .successful([info]))
                        } else if let infos = jsonData as? [[String: Any]] {
                            continuation.resume(returning: .successful(infos))
                        } else {
                            continuation.resume(returning: .successful([["value": jsonData]]))
                        }
                        return
                    case .failure(let error):
                        continuation.resume(returning: analysisNetworkError(config, error: error))
                        return
                    }
                }
            })
        }
    }
    
    //TODO: - 解析网络错误返回
    private func analysisNetworkError<T>(_ config: RequestConfig, error: AFError) -> UorInXNetworkResponseResult<T> {
        if let underlyingError = error.underlyingError, let urlError = underlyingError as? URLError {
            let errorDesc = urlError.localizedDescription
            if urlError.code == .timedOut {
                AF.cancelAllRequests()
                toast(error: "Time Out")
            } else {
                toast(error: errorDesc)
            }
            let statusCode = urlError.code.rawValue
            return .error(UorInXNetworkError(code: statusCode, descripton: errorDesc, request: config.request))
        } else {
            toast(error: error.localizedDescription)
            return .error(UorInXNetworkError(code: error.responseCode ?? 0, descripton: error.localizedDescription, request: config.request))
        }
    }
    
    private func toast(error text: String?) {
        //        text?.toast_error
    }
    
}

import CommonCrypto
fileprivate extension String {
    
    func HMAC(key: String) -> String {
        guard let data = Data(base64Encoded: key, options: .ignoreUnknownCharacters), let content = (self as NSString).utf8String else { return "" }
        let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: Int(CC_MD5_DIGEST_LENGTH))
        CCHmac(CCHmacAlgorithm(kCCHmacAlgMD5), data.bytes, 64, content, strlen(content), result)
        let HMACData = Data(bytes: result, count:  Int(CC_MD5_DIGEST_LENGTH)).base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))
        /// 一定要释放内存
        result.deallocate()
        return HMACData
    }
    
    func convertInfo() -> NSDictionary? {
        let string = (self as NSString).trimmingCharacters(in: CharacterSet.controlCharacters)
        guard let jsonData: Data = string.data(using: .utf8) else { return nil }
        if let dict = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) {
            return dict as? NSDictionary
        }
        return nil
    }
    
}
